\section{Blockbuster}
\label{source_code:blockbuster}
Blockbuster is a binder class that compiles functionality from Encoder, Decoder, Serializer and Network Interface in to one simple-to-use class.

\subsection{blockbuster.hpp}
\begin{lstlisting}[language=C++,numbers=left, numberstyle=\tiny\color{gray}, stepnumber=2, numbersep=5pt, backgroundcolor=\color{white}, morekeywords={*,uint8_t,uint16_t,uint32_t,uint64_t}]
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}

#include <server/kodo_encoder.h>
#include <node/kodo_decoder.h>
#include <postoffice/Postoffice.h>
#include <serializer/serializer.hpp>

#include <vector>
#include <utility>
#include <stdlib.h>
#include <cstring>
#include <cmath>
#include <assert.h>
#include <boost/signals2.hpp>
#include <boost/thread.hpp>

class blockbuster
{
private:
    postoffice *benjamin_krebs;
    kodo_encoder *m_kodo_encoder;
    kodo_decoder *m_kodo_decoder;

    serializer *m_serializer;
    std::vector<uint32_t> serialized_buffer_table;

    bool inbound;
    bool mailbox_active;

    boost::thread transmission_thread;
    boost::thread mailboxthread;

    uint32_t layers;
    uint32_t field_size;
    uint32_t generation_size;
    uint32_t symbol_size;
    uint32_t max_packet_size;
    std::vector<uint32_t> layer_sizes;

    void serialize_avpacket(AVPacket* pkt); // Takes an avpacket and treats it with care and respect, before serializing it with a bunch of other avpackets.
    void make_avpacket(uint8_t*, uint32_t);
    void make_layers(uint32_t nb_layers, uint32_t gsize, uint32_t symb_size);

    uint32_t calculate_generation_size_from_gop_size(uint32_t gop_size);
    uint32_t calculate_symbol_size_from_generation_size(uint32_t generation_size);
    uint32_t calculate_layer_size(uint32_t layer_size);

public:

    blockbuster(bool);
    ~blockbuster();
    boost::signals2::signal<void (AVPacket*)> signal_new_avpacket;

    void mailbox_thread();
    void disconnect();
    void connect_to_stream();

    void transmit_generation(uint32_t,uint32_t,float);

    void prepare_for_kodo_encoder(AVPacket*);
};
\end{lstlisting}

\subsection{blockbuster.cpp}
\begin{lstlisting}[language=C++,numbers=left, numberstyle=\tiny\color{gray}, stepnumber=2, numbersep=5pt, backgroundcolor=\color{white}, morekeywords={*,uint8_t,uint16_t,uint32_t,uint64_t}]
#include "blockbuster.hpp"

blockbuster::blockbuster(bool inbound)
{
    if(inbound)
    {
        m_kodo_decoder = new kodo_decoder();
        benjamin_krebs = new postoffice("11000",1);
    }
    else {
        m_kodo_encoder = new kodo_encoder();
        benjamin_krebs = new postoffice("11000", "255.255.255.255");
    }
    this->inbound = inbound;
    m_serializer = new serializer();
    m_serializer->signal_new_buffer.connect( boost::bind( &blockbuster::make_avpacket, this,_1,_2) );

    max_packet_size = 1450;
}

blockbuster::~blockbuster()
{
    if(inbound)
    {
        disconnect();
        delete m_kodo_decoder;
    }
    else
    {
        delete m_kodo_encoder;
    }
    delete benjamin_krebs;
    delete m_serializer;
}

void blockbuster::serialize_avpacket(AVPacket *pkt)
{
    uint32_t index = m_serializer->feed(pkt->data, pkt->size);
    serialized_buffer_table.push_back(index);
}

void blockbuster::make_avpacket(uint8_t* ptr, uint32_t size)
{
    AVPacket new_avpacket;
    av_init_packet(&new_avpacket);
    new_avpacket.size = size;
    new_avpacket.data = (uint8_t*)av_malloc(size);
    memcpy(new_avpacket.data, ptr, size);

    signal_new_avpacket(&new_avpacket);
}

void blockbuster::prepare_for_kodo_encoder(AVPacket* pkt)
{
    if (!inbound)
    {
        if (pkt->flags == AV_PKT_FLAG_KEY)
        {
            if (m_serializer->size())
            {
                transmission_thread.join(); // Wait for previous transmission thread to finish

                std::vector<uint8_t>& serialized_buffer = m_serializer->serialize();
                // Pass this buffer to kodo encoder. ALONG with other info. 
                uint32_t bufferlength = serialized_buffer.size();

                uint32_t gsize = calculate_generation_size_from_gop_size(bufferlength);
                uint32_t symb_size = calculate_symbol_size_from_generation_size(gsize);

                assert(symb_size*gsize >= bufferlength);
                uint32_t zeropadding_length = symb_size*gsize - bufferlength;
                std::vector<uint8_t> zeropadding(zeropadding_length);
                serialized_buffer.insert(serialized_buffer.end(),zeropadding.begin(),zeropadding.end());
                // Check the length:
                assert(serialized_buffer.size()==symb_size*gsize);

                make_layers(4,gsize,symb_size);
                m_kodo_encoder->new_generation((char*)&serialized_buffer[0]);

                std::cout << "transmitting generation w ID: "<< 1*m_kodo_encoder->payload_stamp.Generation_ID << std::endl;
                transmission_thread = boost::thread( &blockbuster::transmit_generation, this, symb_size, gsize, 3. );

                // Pass to kodo encoder:
                m_serializer->reset(serialized_buffer.size()); // Reset, and allocate a new vector with same size as previous
                serialized_buffer_table.clear();
            }
        }
        serialize_avpacket(pkt);
    }
//    pass frame to serializer.
//    Keep track of how to do UEP on the vector (Where to split the shit in windows)
}

void blockbuster::make_layers(uint32_t nb_layers, uint32_t gsize, uint32_t symb_size)
{
    m_kodo_encoder->set_generation_size(gsize);
    m_kodo_encoder->set_symbol_size(symb_size);

    uint32_t first_layer;
    first_layer = std::ceil(serialized_buffer_table[1]/(float)symb_size);

    if (first_layer < 32) first_layer = 32;
    else first_layer = calculate_layer_size(first_layer);

    if (first_layer > gsize)
    {
        first_layer = gsize;
        nb_layers = 1;
    }

    m_kodo_encoder->set_layers(nb_layers);

    uint32_t first_gamma = std::ceil(first_layer*100/(float)gsize);
    m_kodo_encoder->set_layer_size(1,first_layer);
    m_kodo_encoder->set_layer_gamma(1,first_gamma);

    std::cout << "First layer: " << first_layer;

    uint32_t p_layer_size;
    if (nb_layers > 1)
        p_layer_size = std::ceil( (gsize-first_layer)/(float)(nb_layers-1) );
    for (uint32_t layer = 2; layer < nb_layers ; layer++)
    {
        uint32_t this_layer_size = p_layer_size*(layer-1)+first_layer;
        this_layer_size = calculate_layer_size(this_layer_size);
        if (this_layer_size > gsize) this_layer_size = gsize;
        std::cout << " Layer: " << layer << " has size: " << this_layer_size;
        m_kodo_encoder->set_layer_size(layer,  this_layer_size  );
        m_kodo_encoder->set_layer_gamma(layer, std::ceil(this_layer_size*100/(float)gsize) );
    }
    std::cout << " gsize: " << gsize << std::endl;
    m_kodo_encoder->set_layer_size(nb_layers,gsize);
    m_kodo_encoder->set_layer_gamma(nb_layers,100);
}

void blockbuster::connect_to_stream()
{
    std::cout << "starting mailbox thread\n";
    mailboxthread = boost::thread( &blockbuster::mailbox_thread, this );
}


void blockbuster::disconnect()
{
    mailbox_active = false;
    mailboxthread.join();
    std::cout << "mailboxthread terminated..\n";
}

void blockbuster::mailbox_thread()
{
    mailbox_active = true;
    stamp *hdr = (stamp*)malloc(sizeof(stamp));
    int decoded_generation = 0;
    serial_data received_data;
    m_kodo_decoder->status_output = false;
    int data_array_size = 2000;
    char data_array[data_array_size];
    std::vector<uint8_t> decode_return;

    while(mailbox_active)
    {
        received_data.size = benjamin_krebs->receive(data_array, hdr);
        if(!received_data.size)
        {
            boost::this_thread::sleep(boost::posix_time::milliseconds(100) );
            continue;
        }
        received_data.data = data_array;
        if (m_kodo_decoder->get_current_generation_id() != hdr->Generation_ID && !m_kodo_decoder->has_finished_decoding())
        {
            std::cout << "failed to decode generation " << m_kodo_decoder->get_current_generation_id()*1 << std::endl;
            std::cout << "Layer 1: " << m_kodo_decoder->is_layer_finish(1)*1 << " Layer 2: " << m_kodo_decoder->is_layer_finish(2)*1 << " Layer 3: " << m_kodo_decoder->is_layer_finish(3)*1 << std::endl;
        }
        if (hdr->Generation_ID != decoded_generation) decode_return = m_kodo_decoder->decode(hdr,received_data);
        if(m_kodo_decoder->has_finished_decoding() && m_kodo_decoder->get_current_generation_id() != decoded_generation)
        {
            std::cout << "Decoder finished layer: " << m_kodo_decoder->has_finished_decoding()*1;
            std::cout << " Gopsize: " << hdr->Generation_Size << " (symbols) Symbolsize: " << hdr->Symbol_Size << std::endl;
            m_serializer->deserialize_signal(decode_return);
            decoded_generation = m_kodo_decoder->get_current_generation_id();
        }
    }
}

void blockbuster::transmit_generation(uint32_t symb_size, uint32_t gen_size, float overhead)
{
    assert(overhead >= 1);
    uint32_t totaldata = symb_size*gen_size*overhead;
    for(uint32_t sent_data_size = 0 ; sent_data_size < totaldata ; sent_data_size += symb_size)
    {
        serial_data packet = m_kodo_encoder->get_packet();
        int return_value = benjamin_krebs->send(packet, &(m_kodo_encoder->payload_stamp));
        if (return_value)
            std::cout << std::endl << "ERROR: " << return_value*1 << std::endl;
        assert(!return_value);
    }
}

uint32_t blockbuster::calculate_generation_size_from_gop_size(uint32_t gop_size)
{
    return std::ceil(4*max_packet_size-2*sqrt(4*(max_packet_size*max_packet_size)-2*gop_size) );
}
uint32_t blockbuster::calculate_symbol_size_from_generation_size(uint32_t generation_size)
{
    return max_packet_size-generation_size/8+1;
}
uint32_t blockbuster::calculate_layer_size(uint32_t layer_size)
{
    uint32_t addition = 0;
    if ( layer_size%8 ) addition = (8-layer_size%8);
    return layer_size + addition;
}
\end{lstlisting}